#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <omnetpp.h>

#include <iostream>
#include <string>

#include "mpls_qos.h"
#include "MPLSPacket.h"

#include "RAR_m.h"
#include "RAA_m.h"
#include "LSP_Cost_Update_m.h"

using namespace std;

typedef struct Routing_Table
{
	int 	LSP_ID;				// Is Used to describe the predefined path
	int 	Link_Cost;			// Describes the Cost which is dependent linearly utilization
	int 	Cost;				// Total LSP cost
	int 	Traffic_Rate;		// IR output Traffic
	int 	InOut_Gate;			// The output gate that connects to next CR

	cOutVector CostVector;
	cOutVector LSPLoadVector;
	cOutVector Rejection;

}Routing_Table;

/* Traffic generator structure that memorizes the durationa and rate of QOS request */
typedef struct Node_Traffic
{
	int		  Duration;
	int 	  Rate;
	bool 	  Generate; //Decide whether we want to generate traffic accordingly...
} Node_Traffic;


class Ingress_Router : public cSimpleModule
{
  private:
    simtime_t timeout;
    cMessage *QOStimeoutEvent;
    cMessage *EndOfSimulation;
    cMessage *TrafficGenerator;
    int seq;
    cMessage *message;

    /* Number of packets going through the router */
    double 	  Total_packets, Received;

    /* This will be our total rejected packets counter */
    double Rejected;

  public:
    Ingress_Router();
    virtual ~Ingress_Router();

    Routing_Table Core_Routing_Table[10];
    Routing_Table & Get_Core_Routing_Table(int index) { return Core_Routing_Table[index]; };

    Node_Traffic Traffic_Generator[15];
    Node_Traffic &  Get_Traffic_Generator(int index) { return Traffic_Generator[index]; }

  protected:
    virtual RAR 		   *generateNewRAR_Message(int LSP_ID, int Requested_Bandwidth);
    MPLSPacket 			   *generateNewMPLS_Message(int LSP_ID);
    virtual void 			initialize();
    virtual void 			handleMessage(cMessage *msg);
	virtual void            handleMessage(MPLSPacket *msg);
	virtual void            handleMessage(RAA *msg);
	virtual void            handleMessage(LSP_Cost_Update *msg);
	virtual int				Get_Next_LSP ();
	virtual int				Get_Output_Gate(int LSP_ID);
	virtual void 			Update_Rate(void) { Total_packets = Received; };
};

Define_Module(Ingress_Router);

void Ingress_Router::handleMessage(MPLSPacket *msg) {}
void Ingress_Router::handleMessage(RAA *msg) {}
void Ingress_Router::handleMessage(LSP_Cost_Update *msg) {}


Ingress_Router::Ingress_Router()
{
    Rejected = 0;
	QOStimeoutEvent = message = NULL;
}

Ingress_Router::~Ingress_Router()
{
    delete message;
}

int Ingress_Router::Get_Output_Gate(int LSP_ID)
{
	for (int i=0; i<3; i++)
	{
		if ( Get_Core_Routing_Table(i).LSP_ID == LSP_ID )
		{
			return i;
		}
	}
	return 0;
}

int Ingress_Router::Get_Next_LSP ()
{
	int index;

	if ( (Get_Core_Routing_Table(0).Cost <= Get_Core_Routing_Table(1).Cost) &&
			(Get_Core_Routing_Table(0).Cost <= Get_Core_Routing_Table(2).Cost) )
		index = 0;

	else if ( (Get_Core_Routing_Table(1).Cost <= Get_Core_Routing_Table(0).Cost) &&
			(Get_Core_Routing_Table(1).Cost <= Get_Core_Routing_Table(2).Cost) )
		index = 1;

	else if ( (Get_Core_Routing_Table(2).Cost <= Get_Core_Routing_Table(1).Cost) &&
			(Get_Core_Routing_Table(2).Cost <= Get_Core_Routing_Table(0).Cost) )
		index = 2;


	return index;
}
void Ingress_Router::initialize()
{
	Total_packets = 0;

	for(int i = 0; i < 10; i++)
		{
			    Get_Core_Routing_Table(i).LSP_ID 		= intuniform(1,9);
			    Get_Core_Routing_Table(i).Link_Cost 	= -1;
			    Get_Core_Routing_Table(i).InOut_Gate 	= -1;
			    Get_Core_Routing_Table(i).Link_Cost 	=  0;
			    Get_Core_Routing_Table(i).Cost 			=  0;
			    Get_Core_Routing_Table(i).Traffic_Rate 	=  0;

		}

	for(int i = 0; i < 15; i++)
		{
		Get_Traffic_Generator(i).Generate = true;
		Get_Traffic_Generator(i).Duration = exponential(5);
		Get_Traffic_Generator(i).Rate = intuniform(1,50);
		}

	if (strcmp("IR1", getName()) == 0)
	    {

			Get_Core_Routing_Table(0).LSP_ID         = 1;
			Get_Core_Routing_Table(0).Link_Cost      = 0;
			Get_Core_Routing_Table(0).InOut_Gate     = 0;
			Get_Core_Routing_Table(0).CostVector.setName("CST1");
			Get_Core_Routing_Table(0).LSPLoadVector.setName("LSP1");
			Get_Core_Routing_Table(0).Rejection.setName("Reject1");

			Get_Core_Routing_Table(1).LSP_ID         = 2;
			Get_Core_Routing_Table(1).Link_Cost      = 0;
			Get_Core_Routing_Table(1).InOut_Gate     = 1;
			Get_Core_Routing_Table(1).CostVector.setName("CST2");
			Get_Core_Routing_Table(1).LSPLoadVector.setName("LSP2");
			Get_Core_Routing_Table(1).Rejection.setName("Reject2");

			Get_Core_Routing_Table(2).LSP_ID         = 3;
			Get_Core_Routing_Table(2).Link_Cost      = 0;
			Get_Core_Routing_Table(2).InOut_Gate     = 1;
			Get_Core_Routing_Table(2).CostVector.setName("CST3");
			Get_Core_Routing_Table(2).LSPLoadVector.setName("LSP3");
			Get_Core_Routing_Table(2).Rejection.setName("Reject3");
	    }
	else if (strcmp("IR2", getName()) == 0)
	    {

		Get_Core_Routing_Table(0).LSP_ID         = 4;
		Get_Core_Routing_Table(0).Link_Cost      = 0;
		Get_Core_Routing_Table(0).InOut_Gate     = 0;
		Get_Core_Routing_Table(0).CostVector.setName("CST4");
		Get_Core_Routing_Table(0).LSPLoadVector.setName("LSP4");
		Get_Core_Routing_Table(0).Rejection.setName("Reject4");

		Get_Core_Routing_Table(1).LSP_ID         = 5;
		Get_Core_Routing_Table(1).Link_Cost      = 0;
		Get_Core_Routing_Table(1).InOut_Gate     = 1;
		Get_Core_Routing_Table(1).CostVector.setName("CST5");
		Get_Core_Routing_Table(1).LSPLoadVector.setName("LSP5");
		Get_Core_Routing_Table(1).Rejection.setName("Reject5");

		Get_Core_Routing_Table(2).LSP_ID         = 6;
		Get_Core_Routing_Table(2).Link_Cost      = 0;
		Get_Core_Routing_Table(2).InOut_Gate     = 1;
		Get_Core_Routing_Table(2).CostVector.setName("CST6");
		Get_Core_Routing_Table(2).LSPLoadVector.setName("LSP6");
		Get_Core_Routing_Table(2).Rejection.setName("Reject6");

	    }
	else if (strcmp("IR3", getName()) == 0)
	    {

		Get_Core_Routing_Table(0).LSP_ID         = 7;
		Get_Core_Routing_Table(0).Link_Cost      = 0;
		Get_Core_Routing_Table(0).InOut_Gate     = 1;
		Get_Core_Routing_Table(0).CostVector.setName("CST7");
		Get_Core_Routing_Table(0).LSPLoadVector.setName("LSP7");
		Get_Core_Routing_Table(0).Rejection.setName("Reject7");

		Get_Core_Routing_Table(1).LSP_ID         = 8;
		Get_Core_Routing_Table(1).Link_Cost      = 0;
		Get_Core_Routing_Table(1).InOut_Gate     = 1;
		Get_Core_Routing_Table(1).CostVector.setName("CST8");
		Get_Core_Routing_Table(1).LSPLoadVector.setName("LSP8");
		Get_Core_Routing_Table(1).Rejection.setName("Reject8");

		Get_Core_Routing_Table(2).LSP_ID         = 9;
		Get_Core_Routing_Table(2).Link_Cost      = 0;
		Get_Core_Routing_Table(2).InOut_Gate     = 1;
		Get_Core_Routing_Table(2).CostVector.setName("CST9");
		Get_Core_Routing_Table(2).LSPLoadVector.setName("LSP9");
		Get_Core_Routing_Table(2).Rejection.setName("Reject9");

	    }

    timeout = 1.0;

    QOStimeoutEvent = new cMessage("QOStimeoutEvent");
    scheduleAt(simTime()+timeout, QOStimeoutEvent);

    TrafficGenerator = new cMessage("TrafficGenerator");
    scheduleAt(simTime()+timeout, TrafficGenerator);

    EndOfSimulation = new cMessage("EndOfSimulation");
    scheduleAt(1.0*5*60, EndOfSimulation);

}

void Ingress_Router::handleMessage(cMessage *msg)
{
	string RAAClass ("RAA");
	string LSP_Cost_UpdateClass ("LSP_Cost_Update");

	if (msg == EndOfSimulation)
	{
		EV << "Simulation Ended\n";
		cancelAndDelete(EndOfSimulation);
		cancelAndDelete(QOStimeoutEvent);
		cancelAndDelete(TrafficGenerator);

		return;
	}

	else if (msg==QOStimeoutEvent)
    {
		/*
		 * In case we receive a QOS request:
		 * we start generate the traffic according to the rate and
		 * we'll send the traffic for as long as the duration
		 */

//		bool Qos_Accepted = false;
//
//		for(int i = 0; i < 15; i++)
//    		{
//
//    				if ( (Get_Traffic_Generator(i).Generate) == false )
//    				{
//    					Get_Traffic_Generator(i).Generate = true;
//        				Get_Traffic_Generator(i).Duration = exponential(60);
//            			Get_Traffic_Generator(i).Rate = intuniform(1,10);
//            			Qos_Accepted = true;
//            			break;
//    				}
//    		}
//
//		if ( Qos_Accepted == false )
//    	{
//    		Rejected++;
//    		Get_Core_Routing_Table(Get_Next_LSP()).Rejection.record( Rejected );
//    	}

        scheduleAt(simTime()+timeout, QOStimeoutEvent);
    }
	else if ( msg == TrafficGenerator )
	{
		int Next_LSP_Used = Get_Next_LSP();

		for(int i = 0; i < 15; i++)
		    		{

		    			if ( ((Get_Traffic_Generator(i).Generate) == true ) && ( Get_Traffic_Generator(i).Duration >= 0 ) )
		    			{

		    				/* Send traffic to the next best LSP */
		    				for (int j = Get_Traffic_Generator(i).Rate; j > 0; j--)
		    					send(generateNewMPLS_Message(Next_LSP_Used+1), "gate$o", Get_Core_Routing_Table(Get_Output_Gate(Next_LSP_Used)).InOut_Gate);

		    				Get_Core_Routing_Table(Next_LSP_Used).Traffic_Rate += Get_Traffic_Generator(i).Rate;
		    				Get_Core_Routing_Table(Next_LSP_Used).Cost = Get_Traffic_Generator(i).Rate;
							Get_Core_Routing_Table(Next_LSP_Used).Link_Cost = Get_Traffic_Generator(i).Rate;

		    				Get_Traffic_Generator(i).Duration--;

		    			}

		    			if ( Get_Traffic_Generator(i).Duration <= 0)
	        			{
	        				Get_Core_Routing_Table(Next_LSP_Used).Traffic_Rate -= Get_Traffic_Generator(i).Rate;
	        				Get_Traffic_Generator(i).Generate = false;
//	        				Get_Core_Routing_Table(Next_LSP_Used).Cost =
	        						Get_Core_Routing_Table(Next_LSP_Used).Link_Cost = 0;
	        			}

		    		}

		scheduleAt(simTime()+timeout, TrafficGenerator);
	}

	else
    {
    	if ( !LSP_Cost_UpdateClass.compare(msg->getClassName()) )
    	{
    		LSP_Cost_Update *ttmsg = check_and_cast<LSP_Cost_Update *>(msg);

    		for (int i = 0; i < 3; i++)
    		    			{

    		    				if ( Get_Core_Routing_Table(i).LSP_ID == ttmsg->getLSP_ID()  )
    		    				{

    		    					EV << "LSP Cost received at " << getName() << "\n" << "at gate: " <<
    		    									ttmsg->getArrivalGate()->getIndex() << "\n" <<
    		    									"with LSP: " << ttmsg->getLSP_ID() << " and cost: " <<
    		    									ttmsg->getCost() <<"\n";

    		    					Get_Core_Routing_Table(i).Link_Cost = ttmsg->getCost();
    		    					Get_Core_Routing_Table(i).Cost = ttmsg->getCost();
    		    					Get_Core_Routing_Table(i).CostVector.record(ttmsg->getCost());
    		    					Get_Core_Routing_Table(i).LSPLoadVector.record(Get_Core_Routing_Table(i).Traffic_Rate);
    		    					delete msg;
    		    					break;
    		    				}
    		    			}
    	}

    	else if ( !RAAClass.compare(msg->getClassName()) )
    		{
    			RAA *ttmsg = check_and_cast<RAA *>(msg);

    			for (int i = 0; i < 3; i++)
    			{

    				if ( Get_Core_Routing_Table(i).LSP_ID == ttmsg->getLSP_ID()  )
    				{

#ifdef DEBUG
    					EV << "RAA received at " << getName() << "\n" << "at gate: " <<
    									ttmsg->getArrivalGate()->getIndex() << "\n" <<
    									"with LSP: " << ttmsg->getLSP_ID() << "\n";
#endif

						Get_Core_Routing_Table(i).Traffic_Rate -= ttmsg->getRequested_Bandwidth();
    					delete msg;
    					break;
    				}
    			}

    		}
    }
}

RAR *Ingress_Router::generateNewRAR_Message(int LSP_ID, int Requested_Bandwidth)
{
	RAR *msg = new RAR();

	msg->setLSP_ID(LSP_ID);
	msg->setRequested_Bandwidth(Requested_Bandwidth);

	return msg;
}

MPLSPacket *Ingress_Router::generateNewMPLS_Message(int LSP_ID)
{
	MPLSPacket *msg = new MPLSPacket("Traffic");

		msg->pushLabel(LSP_ID);

	return msg;
}
